package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserEnterpriseService;
import com.evil.account.api.RemoteUserService;
import com.evil.application.api.RemoteApplicationService;
import com.evil.application.constant.ApplicationConstant;
import com.evil.application.mapper.ApplicationGroupMapper;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.mapper.ApplicationPackageMapper;
import com.evil.application.mapper.FavouriteApplicationMapper;
import com.evil.application.pojo.dto.*;
import com.evil.application.pojo.dto.group.ApplicationGroupReqDTO;
import com.evil.application.pojo.entity.*;
import com.evil.application.service.ApplicationInfoService;
import com.evil.application.service.ApplicationPermissionService;
import com.evil.application.service.FavouriteApplicationService;
import com.evil.application.service.PackageApplicationService;
import com.evil.application.strategy.application.ApplicationOperationStrategy;
import com.evil.application.util.ApplicationUtil;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.account.dto.user.UserEnterpriseDTO;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.permission.app.*;
import com.evil.common.application.dto.pkg.ModifyPackageApplicationSourceDTO;
import com.evil.common.application.dto.pkg.PackageApplicationSourceDTO;
import com.evil.common.application.dto.pkg.PackageApplicationSourcesDTO;
import com.evil.common.application.enums.ApplicationSourceTypeEnum;
import com.evil.common.application.enums.ApplicationTypeEnum;
import com.evil.common.application.enums.PublishStatusEnum;
import com.evil.common.application.enums.permission.PermissionStatusEnum;
import com.evil.common.application.util.ApplicationRedisKeyUtil;
import com.evil.common.core.dto.FindByIdsReqDTO;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.CacheUtil;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.file.dto.FileRespDTO;
import com.evil.file.api.RemoteFileService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationInfoServiceImpl extends ServiceImpl<ApplicationInfoMapper, ApplicationInfo> implements ApplicationInfoService, RemoteApplicationService {

    private final LoginUtil loginUtil;

    private final CacheUtil cacheUtil;

    private final ApplicationPackageMapper applicationPackageMapper;

    private final ApplicationGroupMapper applicationGroupMapper;

    private final FavouriteApplicationMapper favouriteApplicationMapper;

    private final PackageApplicationService packageApplicationService;

    private final ApplicationPermissionService applicationPermissionService;

    private final FavouriteApplicationService favouriteApplicationService;

    private final ApplicationOperationStrategy applicationOperationStrategy;

    private final RemoteUserEnterpriseService remoteUserEnterpriseService;

    private final RemoteUserService remoteUserService;

    private final RemoteFileService remoteFileService;

    private final ApplicationLogServiceImpl applicationLogService;

    /**
     * 应用列表
     *
     * @param applicationReqDTO applicationReqDTO
     */
    @Override
    public List<ApplicationInfoRespDTO> applicationList(ApplicationReqDTO applicationReqDTO) {
        UserEnterpriseDTO loginUserEnterprise = loginUtil.getLoginUserEnterprise();
        Long loginEnterpriseId = loginUserEnterprise.getEnterpriseId();
        Long loginUserId = loginUserEnterprise.getUserId();
        applicationReqDTO.setEnterpriseId(loginUtil.getParamEnterpriseId(applicationReqDTO.getEnterpriseId()));

        // 是否是企业管理员
        boolean enterpriseManager = remoteUserEnterpriseService.enterpriseManager(loginUserEnterprise);

        // 用户常用应用
        Map<Long, Integer> appFavouriteMap = StreamUtil.toMapKV(favouriteApplicationMapper.findByUserId(loginEnterpriseId, loginUserId, null),
                FavouriteApplication::getApplicationId, FavouriteApplication::getFavourite);

        List<ApplicationInfo> apps = this.baseMapper.findByApplicationReqDTO(applicationReqDTO);
        // 过滤权限
        apps = StreamUtil.filter(apps, app -> applicationPermissionService.loginUserHasPermission(
                app.getEnterpriseId(),
                app.getPermissionStatus(),
                new AppPermissionSourceDTO(ApplicationSourceTypeEnum.APPLICATION, app.getApplicationId()),
                false));

        // 获取icon
        Set<Long> fileIds = StreamUtil.transSetFT(apps, app -> Objects.nonNull(app.getIcon()), ApplicationInfo::getIcon);
        Map<Long, FileRespDTO> iconFileBaseMap = remoteFileService.findBaseMapByFileIds(fileIds);

        // 转换返回实体
        return StreamUtil.transListT(apps, app -> {
            ApplicationInfoRespDTO applicationInfoRespDTO = BeanUtil.copyProperties(app, ApplicationInfoRespDTO.class);
            // icon
            Optional.ofNullable(app.getIcon()).ifPresent(f -> applicationInfoRespDTO.setIcon(iconFileBaseMap.get(f)));
            applicationInfoRespDTO.setIsFavourite(appFavouriteMap.getOrDefault(app.getApplicationId(), SwitchEnum.NO.getId()));
            applicationInfoRespDTO.setManager(enterpriseManager);
            return applicationInfoRespDTO;
        });
    }

    /**
     * 应用基础信息列表
     *
     * @param applicationReqDTO applicationReqDTO
     */
    @Override
    public List<ApplicationBaseInfoRespDTO> applicationBases(ApplicationReqDTO applicationReqDTO) {
        applicationReqDTO.setEnterpriseId(loginUtil.getParamEnterpriseId(applicationReqDTO.getEnterpriseId()));

        List<ApplicationInfo> apps = this.baseMapper.findByApplicationReqDTO(applicationReqDTO);

        List<Long> appIds = StreamUtil.transListT(apps, ApplicationInfo::getApplicationId);
        // 获取应用权限
        Map<Long, List<AppPermissionDTO>> appPermissionMap = applicationPermissionService.findAppPermissionsBySource(
                new AppPermissionsReqDTO(ApplicationSourceTypeEnum.APPLICATION, appIds));
        // 获取应用包列表
        Map<Long, List<Long>> appPackageIdsMap = packageApplicationService.findPackageIdsMapBySources(
                new PackageApplicationSourcesDTO(ApplicationSourceTypeEnum.APPLICATION, appIds));

        // icon
        Set<Long> fileIds = StreamUtil.transSetFT(apps, app -> Objects.nonNull(app.getIcon()), ApplicationInfo::getIcon);
        Map<Long, FileRespDTO> iconFileBaseMap = remoteFileService.findBaseMapByFileIds(fileIds);

        // 创建人
        List<Long> creatorIds = StreamUtil.transListT(apps, ApplicationInfo::getCreatorId);
        Map<Long, UserBaseRespDTO> userBaseMap = remoteUserService.findBaseMapByIds(FindByIdsReqDTO.unFilter(creatorIds));

        return StreamUtil.transListT(apps, app -> {
            ApplicationBaseInfoRespDTO base = BeanUtil.copyProperties(app, ApplicationBaseInfoRespDTO.class);
            base.setPermissions(appPermissionMap.getOrDefault(app.getApplicationId(), new ArrayList<>()));
            base.setPackageIds(appPackageIdsMap.getOrDefault(app.getApplicationId(), new ArrayList<>()));
            Optional.ofNullable(app.getIcon()).ifPresent(f -> base.setIcon(iconFileBaseMap.get(f)));
            base.setCreator(userBaseMap.get(app.getCreatorId()));
            return base;
        });
    }

    /**
     * 应用树
     *
     * @param applicationReqDTO applicationReqDTO
     */
    @Override
    public List<AppTreeDTO> applicationTree(ApplicationReqDTO applicationReqDTO) {
        // 获取 应用包应用列表
        List<PackageApplication> packageApplicationList = packageApplicationService.findByApplicationReqDTO(applicationReqDTO);

        // 过滤 可见应用包
        List<Long> packageIds = StreamUtil.transListT(packageApplicationList, PackageApplication::getPackageId);
        List<ApplicationPackage> packages = applicationPackageMapper.findByIds(packageIds, true);
        // 过滤权限可见
        packages = StreamUtil.filter(packages, e -> applicationPermissionService.loginUserHasPermission(
                e.getEnterpriseId(),
                e.getPermissionStatus(),
                new AppPermissionSourceDTO(ApplicationSourceTypeEnum.PACKAGE, e.getPackageId()),
                false));
        Map<Long, ApplicationPackage> packageMap = StreamUtil.toMapK(packages, ApplicationPackage::getPackageId);
        packageApplicationList = StreamUtil.filter(packageApplicationList, e -> packageMap.containsKey(e.getPackageId()));

        // 包应用map
        Map<Integer, List<Long>> sourceTypeMap = StreamUtil.groupAndThenTrans(packageApplicationList,
                PackageApplication::getSourceType, PackageApplication::getSourceId);

        // 记录icon文件ids
        Set<Long> fileIds = new HashSet<>();
        // 获取应用组
        Map<Long, ApplicationGroup> groupMap = new HashMap<>();
        List<Long> applicationGroupIds = sourceTypeMap.getOrDefault(ApplicationSourceTypeEnum.GROUP.getId(), new ArrayList<>());
        if (StringUtils.isBlank(applicationReqDTO.getApplicationName())
                && StringUtils.isBlank(applicationReqDTO.getApplicationKey())
                && !applicationGroupIds.isEmpty()) {
            ApplicationGroupReqDTO applicationGroupReqDTO = new ApplicationGroupReqDTO();
            applicationGroupReqDTO.setApplicationGroupIds(applicationGroupIds);
            applicationGroupReqDTO.setApplicationGroupName(applicationReqDTO.getApplicationName());
            applicationGroupReqDTO.setIsDeleted(SwitchEnum.NO.getId());
            List<ApplicationGroup> groups = applicationGroupMapper.findByApplicationGroupReqDTO(applicationGroupReqDTO);
            groupMap.putAll(StreamUtil.toMapK(groups, ApplicationGroup::getApplicationGroupId));
            groups.forEach(g -> Optional.ofNullable(g.getIcon()).ifPresent(fileIds::add));
        }

        // 获取可见应用
        Map<Long, ApplicationInfo> appMap = new HashMap<>();
        List<Long> applicationIds = sourceTypeMap.getOrDefault(ApplicationSourceTypeEnum.APPLICATION.getId(), new ArrayList<>());
        if (!applicationIds.isEmpty()) {
            ApplicationInfoReqDTO applicationInfoReqDTO = new ApplicationInfoReqDTO();
            applicationInfoReqDTO.setApplicationIds(applicationIds);
            applicationInfoReqDTO.setApplicationKey(applicationReqDTO.getApplicationKey());
            applicationInfoReqDTO.setApplicationName(applicationReqDTO.getApplicationName());
            applicationInfoReqDTO.setApplicationTitle(applicationReqDTO.getApplicationTitle());
            applicationInfoReqDTO.setApplicationType(applicationReqDTO.getApplicationType());
            applicationInfoReqDTO.setIsDeleted(SwitchEnum.NO.getId());
            List<ApplicationInfo> apps = this.baseMapper.findByApplicationInfoReqDTO(applicationInfoReqDTO);
            // 过滤权限可见
            apps = StreamUtil.filter(apps, e -> applicationPermissionService.loginUserHasPermission(
                    e.getEnterpriseId(),
                    e.getPermissionStatus(),
                    new AppPermissionSourceDTO(ApplicationSourceTypeEnum.APPLICATION, e.getApplicationId()),
                    false));
            appMap.putAll(StreamUtil.toMapK(apps, ApplicationInfo::getApplicationId));
            apps.forEach(g -> Optional.ofNullable(g.getIcon()).ifPresent(fileIds::add));
        }

        // 获取icon文件
        Map<Long, FileRespDTO> iconFileBaseMap = remoteFileService.findBaseMapByFileIds(fileIds);

        // 转换item
        List<AppTreeDTO> items = new ArrayList<>();
        packageApplicationList.forEach(e -> {
            if (ApplicationSourceTypeEnum.GROUP.getId() == e.getSourceType()) {
                if (groupMap.containsKey(e.getSourceId())) {
                    items.add(this.toAppTreeDTO(groupMap.get(e.getSourceId()), iconFileBaseMap, e.getSort()));
                }
            } else if (ApplicationSourceTypeEnum.APPLICATION.getId() == e.getSourceType()) {
                if (appMap.containsKey(e.getSourceId())) {
                    items.add(this.toAppTreeDTO(appMap.get(e.getSourceId()), iconFileBaseMap, e.getSort()));
                }
            }
        });

        // 分类
        return ApplicationUtil.classifyItem(items);
    }

    /**
     * 应用信息
     *
     * @param applicationKey 应用key
     */
    @Override
    public ApplicationInfoRespDTO applicationInfo(String applicationKey) {
        UserEnterpriseDTO userEnterpriseDTO = loginUtil.getLoginUserEnterprise();
        ApplicationInfo app = this.checkByKey(applicationKey);

        // 是否是企业管理员
        boolean enterpriseManager = remoteUserEnterpriseService.enterpriseManager(userEnterpriseDTO);

        Integer isFavourite = favouriteApplicationMapper.findUserApp(userEnterpriseDTO.getUserId(), app.getApplicationId())
                .map(FavouriteApplication::getFavourite)
                .orElse(SwitchEnum.NO.getId());

        ApplicationInfoRespDTO applicationInfoRespDTO = BeanUtil.copyProperties(app, ApplicationInfoRespDTO.class);
        // icon
        Optional.ofNullable(app.getIcon()).map(remoteFileService::findBaseByFileId).ifPresent(applicationInfoRespDTO::setIcon);
        applicationInfoRespDTO.setCanAdd(enterpriseManager);
        applicationInfoRespDTO.setCanModify(enterpriseManager);
        applicationInfoRespDTO.setCanDelete(enterpriseManager);
        applicationInfoRespDTO.setIsFavourite(isFavourite);
        applicationInfoRespDTO.setManager(enterpriseManager);
        return applicationInfoRespDTO;
    }

    /**
     * 应用基础信息
     *
     * @param applicationKey applicationKey
     * @return ApplicationBaseInfoRespDTO
     */
    @Override
    public ApplicationBaseInfoRespDTO applicationBaseInfo(String applicationKey) {
        ApplicationInfo applicationInfo = this.findByKey(applicationKey);

        // 获取应用权限
        List<AppPermissionDTO> permissions = applicationPermissionService.findDTOBySource(
                new AppPermissionSourceDTO(ApplicationSourceTypeEnum.APPLICATION.getId(), applicationInfo.getApplicationId()));

        // 获取应用包列表
        List<Long> packageIds = packageApplicationService.findBySource(new PackageApplicationSourceDTO(
                ApplicationSourceTypeEnum.APPLICATION, applicationInfo.getApplicationId())).stream()
                .map(PackageApplication::getPackageId)
                .collect(Collectors.toList());

        ApplicationBaseInfoRespDTO applicationBaseInfoRespDTO = BeanUtil.copyProperties(applicationInfo, ApplicationBaseInfoRespDTO.class);
        applicationBaseInfoRespDTO.setPermissions(permissions);
        applicationBaseInfoRespDTO.setCreator(remoteUserService.findBaseById(applicationInfo.getCreatorId()));
        applicationBaseInfoRespDTO.setPackageIds(packageIds);
        return applicationBaseInfoRespDTO;
    }

    /**
     * 应用发布信息
     *
     * @param applicationKey applicationKey
     * @return ApplicationBaseInfoRespDTO
     */
    @Override
    public ApplicationPublishInfoRespDTO applicationPublishInfo(String applicationKey) {
        ApplicationInfo app = this.findByKey(applicationKey);

        UserBaseRespDTO lastPublisher = remoteUserService.findBaseById(app.getLastPublisherId());
        ApplicationPublishInfoRespDTO applicationPublishInfoRespDTO = BeanUtil.copyProperties(app, ApplicationPublishInfoRespDTO.class);
        applicationPublishInfoRespDTO.setLastPublisher(lastPublisher);
        // icon
        Optional.ofNullable(app.getIcon()).map(remoteFileService::findBaseByFileId).ifPresent(applicationPublishInfoRespDTO::setIcon);
        return applicationPublishInfoRespDTO;
    }

    /**
     * 新增空白应用
     *
     * @param addApplicationReqDTO addApplicationReqDTO
     * @return ApplicationBaseInfoRespDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public ApplicationBaseInfoRespDTO addApplication(AddApplicationReqDTO addApplicationReqDTO) {
        Long loginEnterpriseId = loginUtil.getLoginEnterpriseId();
        String applicationKey = addApplicationReqDTO.getApplicationKey();
        if (StringUtils.isNotBlank(applicationKey)) {
            applicationKey = applicationKey.trim();
            if (applicationKey.length() < 8 || applicationKey.length() > 32) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "应用key错误");
            }
        }

        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationTypeEnum.isExistById(addApplicationReqDTO.getApplicationType());

        List<ApplicationPackage> packageList = applicationPackageMapper.findByIds(addApplicationReqDTO.getPackageIds(), true);
        if (packageList.size() != addApplicationReqDTO.getPackageIds().size()) {
            throw new BusinessException(RCodeEnum.APPLICATION_PACKAGE_NOT_EXIST);
        }

        // 创建应用
        ApplicationInfo applicationInfo = BeanUtil.copyProperties(addApplicationReqDTO, ApplicationInfo.class);
        applicationInfo.setEnterpriseId(loginEnterpriseId);

        if (StringUtils.isNotBlank(applicationKey)) {
            if (this.baseMapper.isExistByKey(applicationKey).isPresent()) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "应用key已存在");
            }
        } else {
            // 生成key
            applicationKey = ApplicationUtil.generateKey(key -> this.baseMapper.isExistByKey(key).isPresent());
        }
        applicationInfo.setApplicationKey(applicationKey);
        applicationInfo.setApplicationGroupId(0L);
        // icon
        if (StringUtils.isNotBlank(addApplicationReqDTO.getIconFileCode())) {
            FileRespDTO iconFileBase = remoteFileService.findBaseByFileCode(addApplicationReqDTO.getIconFileCode());
            applicationInfo.setIcon(iconFileBase.getFileId());
        }
        applicationInfo.setAddBtnText(ApplicationConstant.DEFAULT_ADD_TEXT);
        applicationInfo.setPublishStatus(PublishStatusEnum.UN_PUBLISH.getId());
        applicationInfo.setLastPublish(new Date());
        applicationInfo.setLastPublisherId(loginUtil.getLoginUserId());

        applicationInfo.setIsEnable(SwitchEnum.YES.getId());
        applicationInfo.setApplyEnable(SwitchEnum.YES.getId());
        applicationInfo.setIsDeleted(SwitchEnum.NO.getId());

        applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);

        // 应用包应用表
        ModifyPackageApplicationSourceDTO modifyPackageApplicationReqDTO = new ModifyPackageApplicationSourceDTO();
        modifyPackageApplicationReqDTO.setSourceType(ApplicationSourceTypeEnum.APPLICATION.getId());
        modifyPackageApplicationReqDTO.setSourceId(applicationInfo.getApplicationId());
        modifyPackageApplicationReqDTO.setPackageIds(addApplicationReqDTO.getPackageIds());
        packageApplicationService.savePackageApplications(modifyPackageApplicationReqDTO);

        // 保存应用权限
        this.savePermission(applicationInfo, addApplicationReqDTO.getPermissions());

        // 应用操作策略：新增应用
        applicationOperationStrategy.addApplication(applicationInfo);

        return this.applicationBaseInfo(applicationInfo.getApplicationKey());
    }

    /**
     * 编辑应用
     *
     * @param modifyApplicationReqDTO modifyApplicationReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void modifyApplication(ModifyApplicationReqDTO modifyApplicationReqDTO) {
        String applicationKey = modifyApplicationReqDTO.getApplicationKey();
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationTypeEnum applicationTypeEnum = ApplicationTypeEnum.isExistById(modifyApplicationReqDTO.getApplicationType());

        // 回调操作（清理缓存）
        cacheUtil.clearRequestCache(() -> {
            // 禁止使用缓存
            cacheUtil.cantUseCache();

            ApplicationInfo applicationInfo = this.checkByKey(applicationKey);

            // 非视图应用，不允许修改应用名称
            if (ApplicationTypeEnum.VIEW_APP.getId() != applicationTypeEnum.getId()) {
                modifyApplicationReqDTO.setApplicationName(applicationInfo.getApplicationName());
            }

            BeanUtil.copyProperties(modifyApplicationReqDTO, applicationInfo);
            applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);

            // 应用包应用表
            ModifyPackageApplicationSourceDTO modifyPackageApplicationReqDTO = new ModifyPackageApplicationSourceDTO();
            modifyPackageApplicationReqDTO.setSourceType(ApplicationSourceTypeEnum.APPLICATION.getId());
            modifyPackageApplicationReqDTO.setSourceId(applicationInfo.getApplicationId());
            modifyPackageApplicationReqDTO.setPackageIds(modifyApplicationReqDTO.getPackageIds());
            packageApplicationService.savePackageApplications(modifyPackageApplicationReqDTO);

            // 保存应用权限
            this.savePermission(applicationInfo, modifyApplicationReqDTO.getPermissions());

            // 记录 移除应用缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.applicationIdKey(applicationInfo.getApplicationId()));
            // 记录 移除应用缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.applicationKey(applicationInfo.getApplicationKey()));
        });
    }

    /**
     * 编辑应用权限
     *
     * @param modifyApplicationPermissionReqDTO modifyAppPermissionReqDTO
     */
    @Override
    public void modifyAppPermission(ModifyApplicationPermissionReqDTO modifyApplicationPermissionReqDTO) {
        String applicationKey = modifyApplicationPermissionReqDTO.getApplicationKey();
        // 回调操作（清理缓存）
        cacheUtil.clearRequestCache(() -> {
            // 禁止使用缓存
            cacheUtil.cantUseCache();

            ApplicationInfo applicationInfo = this.checkByKey(applicationKey);
            // 检查企业
            loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

            applicationInfo.setPermissionStatus(modifyApplicationPermissionReqDTO.getPermissionStatus());

            applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);
            // 保存应用权限
            this.savePermission(applicationInfo, modifyApplicationPermissionReqDTO.getPermissions());

            // 记录 移除应用缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.applicationIdKey(applicationInfo.getApplicationId()));
            // 记录 移除应用缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.applicationKey(applicationInfo.getApplicationKey()));
        });
    }

    /**
     * 复制应用
     *
     * @param copyApplicationReqDTO copyApplicationReqDTO
     */
    @Override
    public void copyApplication(CopyApplicationReqDTO copyApplicationReqDTO) {
        String applicationKey = copyApplicationReqDTO.getApplicationKey();
        Long packageId = copyApplicationReqDTO.getPackageId();

        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 禁止使用缓存
        cacheUtil.cantUseCache();
        ApplicationInfo applicationInfo = this.checkByKey(applicationKey);

        ApplicationInfo copyApplication = BeanUtil.copyProperties(applicationInfo, ApplicationInfo.class);
        copyApplication.setApplicationId(null);

        // 生成key
        String copyApplicationKey = ApplicationUtil.generateKey(key -> this.baseMapper.isExistByKey(key).isPresent());
        copyApplication.setApplicationKey(copyApplicationKey);
        copyApplication.setApplicationName(String.format("副本-%s", copyApplication.getApplicationName()));
        copyApplication.setPublishStatus(PublishStatusEnum.UN_PUBLISH.getId());

        applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, copyApplication);

        // 应用包应用
        if (null != packageId) {
            ApplicationPackage applicationPackage = applicationPackageMapper.findById(packageId);
            packageApplicationService.addPackageApp(applicationPackage, copyApplication);
        }

        // 复制应用权限
        if (PermissionStatusEnum.ASSIGN.getId() == applicationInfo.getPermissionStatus()) {
            ApplicationSourceTypeEnum sourceTypeEnum = ApplicationSourceTypeEnum.APPLICATION;
            Long sourceId = applicationInfo.getApplicationId();
            Long targetId = copyApplication.getApplicationId();
            Long enterpriseId = copyApplication.getEnterpriseId();
            applicationPermissionService.copyPermission(new CopyAppPermissionReqDTO(sourceTypeEnum, enterpriseId, sourceId, targetId));
        }

        // 应用操作策略：复制应用
        applicationOperationStrategy.copyApplication(applicationInfo, copyApplication);
    }

    /**
     * 设为常用应用
     *
     * @param favouriteApplicationReqDTO favouriteApplicationReqDTO
     */
    @Override
    public void favouriteApplication(FavouriteApplicationReqDTO favouriteApplicationReqDTO) {
        String applicationKey = favouriteApplicationReqDTO.getApplicationKey();
        Integer favourite = favouriteApplicationReqDTO.getFavourite();

        ApplicationInfo applicationInfo = this.checkByKey(applicationKey);
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        favouriteApplicationService.favourite(applicationInfo.getApplicationId(), favourite);
    }

    /**
     * 常用应用列表
     */
    @Override
    public List<ApplicationInfoDTO> favouriteApplicationList() {
        UserEnterpriseDTO loginUserEnterprise = loginUtil.getLoginUserEnterprise();
        Long loginEnterpriseId = loginUserEnterprise.getEnterpriseId();
        Long loginUserId = loginUserEnterprise.getUserId();
        List<FavouriteApplication> favouriteApps = favouriteApplicationMapper.findByUserId(loginEnterpriseId, loginUserId, SwitchEnum.YES.getId());

        // 获取应用
        List<Long> applicationIds = StreamUtil.transListT(favouriteApps, FavouriteApplication::getApplicationId);
        List<ApplicationInfo> apps = this.baseMapper.findByIds(applicationIds, true);

        // 获取icon
        Set<Long> fileIds = StreamUtil.transSetFT(apps, a -> Objects.nonNull(a.getIcon()), ApplicationInfo::getIcon);
        Map<Long, FileRespDTO> iconFileBaseMap = remoteFileService.findBaseMapByFileIds(fileIds);

        // 转换返回实体
        return StreamUtil.transListT(apps, app -> {
            ApplicationInfoDTO applicationInfoDTO = BeanUtil.copyProperties(app, ApplicationInfoDTO.class);
            Optional.ofNullable(app.getIcon()).ifPresent(f -> applicationInfoDTO.setIcon(iconFileBaseMap.get(f)));
            return applicationInfoDTO;
        });
    }

    /**
     * 删除应用
     *
     * @param applicationKey applicationKey
     */
    @Override
    public void deleteApplication(String applicationKey) {
        this.delete(this.checkByKey(applicationKey));
    }

    /**
     * 发布应用
     *
     * @param applicationKey applicationKey
     */
    @Override
    public void publishApplication(String applicationKey) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 回调操作（清理缓存）
        cacheUtil.clearRequestCache(() -> {
            // 禁止使用缓存
            cacheUtil.cantUseCache();

            ApplicationInfo applicationInfo = this.checkByKey(applicationKey);
            // 应用操作策略：发布应用
            applicationOperationStrategy.publishApplication(applicationInfo);

            applicationInfo.setPublishStatus(PublishStatusEnum.PUBLISHED.getId());
            applicationInfo.setLastPublish(new Date());
            applicationInfo.setLastPublisherId(loginUtil.getLoginUserId());
            applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);
        });
    }

    /**
     * 还原应用
     *
     * @param applicationKey applicationKey
     */
    @Override
    public void restoreApplication(String applicationKey) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 回调操作（清理缓存）
        cacheUtil.clearRequestCache(() -> {
            // 禁止使用缓存
            cacheUtil.cantUseCache();

            ApplicationInfo applicationInfo = this.checkByKey(applicationKey);
            // 应用操作策略：还原应用
            applicationOperationStrategy.restoreApplication(applicationInfo);

            applicationInfo.setPublishStatus(PublishStatusEnum.PUBLISHED.getId());
            applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);
        });
    }

    /**
     * 编辑应用状态
     *
     * @param modifyApplicationStatusReqDTO modifyApplicationStatusReqDTO
     */
    @Override
    public void modifyApplicationStatus(ModifyApplicationStatusReqDTO modifyApplicationStatusReqDTO) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        String applicationKey = modifyApplicationStatusReqDTO.getApplicationKey();
        Integer isEnable = modifyApplicationStatusReqDTO.getIsEnable();

        ApplicationInfo applicationInfo = this.checkByKey(applicationKey);
        applicationInfo.setIsEnable(isEnable);

        applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);
    }

    /**
     * 编辑应用申请状态
     *
     * @param modifyApplicationApplyReqDTO modifyApplicationApplyReqDTO
     */
    @Override
    public void modifyApplicationApply(ModifyApplicationApplyReqDTO modifyApplicationApplyReqDTO) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        String applicationKey = modifyApplicationApplyReqDTO.getApplicationKey();
        Integer applyEnable = modifyApplicationApplyReqDTO.getApplyEnable();

        ApplicationInfo applicationInfo = this.checkByKey(applicationKey);
        applicationInfo.setApplyEnable(applyEnable);

        applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);
    }

    /**
     * 获取企业并检查企业
     *
     * @param applicationKey applicationKey
     * @return ApplicationInfo
     */
    @Override
    public ApplicationInfo checkByKey(String applicationKey) {
        ApplicationInfo applicationInfo = this.baseMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        return applicationInfo;
    }

    /**
     * 检查应用状态
     *
     * @param applicationInfo applicationInfo
     */
    @Override
    public void checkApplication(ApplicationInfo applicationInfo) {
        if (SwitchEnum.isClose(applicationInfo.getIsEnable())) {
            throw new BusinessException(RCodeEnum.APPLICATION_INFO_DISABLED);
        }
    }

    /**
     * 检查应用申请状态
     *
     * @param applicationInfo applicationInfo
     */
    @Override
    public void checkApplyApplication(ApplicationInfo applicationInfo) {
        this.checkApplication(applicationInfo);

        // 申请状态
        if (SwitchEnum.isClose(applicationInfo.getApplyEnable())) {
            throw new BusinessException(RCodeEnum.APPLICATION_INFO_UN_APPLY);
        }
    }

    /**
     * 删除应用
     *
     * @param id applicationId
     */
    @Override
    public void deleteById(Long id) {
        this.delete(this.baseMapper.findById(id));
    }

    /**
     * 删除应用
     *
     * @param applicationInfo applicationInfo
     */
    @Override
    public void delete(ApplicationInfo applicationInfo) {
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        applicationInfo.setIsDeleted(SwitchEnum.YES.getId());
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationInfo::getApplicationId, applicationInfo);

        // 回调操作（清理缓存）
        cacheUtil.clearRequestCache(() -> {
            // 禁止使用缓存
            cacheUtil.cantUseCache();

            // 删除应用包应用
            packageApplicationService.deletePackageApplication(new PackageApplicationSourceDTO(
                    ApplicationSourceTypeEnum.APPLICATION, applicationInfo.getApplicationId()));

            // 删除应用权限
            applicationPermissionService.deleteBySource(new AppPermissionSourceDTO(
                    ApplicationSourceTypeEnum.APPLICATION, applicationInfo.getApplicationId()));

            // 应用操作策略：删除应用
            applicationOperationStrategy.deleteApplication(applicationInfo);
        });
    }

    /**
     * 获取应用
     *
     * @param applicationKey applicationKey
     * @return ApplicationInfo
     */
    private ApplicationInfo findByKey(String applicationKey) {
        ApplicationInfo applicationInfo = this.baseMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        return applicationInfo;
    }

    /**
     * 应用转 AppTreeDTO
     *
     * @param app  app
     * @param sort sort
     * @return AppTreeDTO
     */
    private AppTreeDTO toAppTreeDTO(ApplicationInfo app, Map<Long, FileRespDTO> iconFileBaseMap, Integer sort) {
        AppTreeDTO appTreeDTO = BeanUtil.copyProperties(app, AppTreeDTO.class);
        appTreeDTO.setIsGroup(false);
        appTreeDTO.setTitle(app.getApplicationName());
        Optional.ofNullable(app.getIcon()).ifPresent(f -> appTreeDTO.setIcon(iconFileBaseMap.get(f)));
        appTreeDTO.setSort(sort);
        return appTreeDTO;
    }

    /**
     * 应用组转 AppTreeDTO
     *
     * @param group group
     * @param sort  sort
     * @return AppTreeDTO
     */
    private AppTreeDTO toAppTreeDTO(ApplicationGroup group, Map<Long, FileRespDTO> iconFileBaseMap, Integer sort) {
        AppTreeDTO appTreeDTO = BeanUtil.copyProperties(group, AppTreeDTO.class);
        appTreeDTO.setIsGroup(true);
        appTreeDTO.setTitle(group.getGroupName());
        Optional.ofNullable(group.getIcon()).ifPresent(f -> appTreeDTO.setIcon(iconFileBaseMap.get(f)));
        appTreeDTO.setSort(sort);
        return appTreeDTO;
    }

    /**
     * 保存应用权限
     *
     * @param applicationInfo applicationInfo
     * @param permissions     permissions
     */
    private void savePermission(ApplicationInfo applicationInfo, List<AppPermissionDTO> permissions) {
        // 保存应用权限
        if (PermissionStatusEnum.ASSIGN.getId() == applicationInfo.getPermissionStatus()) {
            ModifyAppPermissionReqDTO modifyAppPermissionReqDTO = new ModifyAppPermissionReqDTO();
            modifyAppPermissionReqDTO.setEnterpriseId(applicationInfo.getEnterpriseId());
            modifyAppPermissionReqDTO.setSourceType(ApplicationSourceTypeEnum.APPLICATION.getId());
            modifyAppPermissionReqDTO.setSourceId(applicationInfo.getApplicationId());
            modifyAppPermissionReqDTO.setPermissions(permissions);
            applicationPermissionService.modifyPermission(modifyAppPermissionReqDTO);
        }
    }
}
